<script setup lang="ts">
import Designer from './Designer.vue'
import useStore from '../../store/index'
import ControlConfig from './ControlConfig.vue'
import { FormDataEnum } from '../../store/modules/FormData'
import { computed, ref, watch, ComputedRef, Ref } from 'vue'
import { formSettingItems } from './config/FormSettingItems'
import { FormItemsEnum } from '../../store/modules/FormItems'
import {
    CheckboxType,
    ColorType,
    InputNumberType,
    InputType,
    PasswordType,
    RadioType,
    RateType,
    SelectType,
    SliderType,
    SwitchType,
    TextareaType,
    TimePickerType,
    TimeSelectType,
    VirtualizedSelectType,
} from '../../types/ItemMetaTypes'
import { ElMessage } from 'element-plus'
import ItemResolve from './ItemsResolve.vue'

interface PropsType {
    height?: string
    preview?: boolean
    formData?: any
    formItems?: Array<
        | CheckboxType
        | InputType
        | PasswordType
        | TextareaType
        | InputNumberType
        | RadioType
        | RateType
        | SwitchType
        | SliderType
        | ColorType
        | SelectType
        | VirtualizedSelectType
        | TimePickerType
        | TimeSelectType
    >
}

// eslint-disable-next-line no-undef
const props = withDefaults(defineProps<PropsType>(), {
    preview: false,
    height: '96.8vh',
    formData: useStore.state.FormData,
    // @ts-ignore
    formItems: useStore.state.FormItems.items,
})

// eslint-disable-next-line no-undef
const emit = defineEmits(['submit'])

// 提交
const submit = (): void => {
    emit('submit')
}

// 在默认情况下不重新设置vuex中的数据,以防循环依赖
if (props.formItems !== useStore.state.FormItems.items) {
    // 设置vuex中的formItems
    useStore.dispatch(FormItemsEnum.ModuleFunctionName(FormItemsEnum.RESET_FORM_ITEMS), props.formItems)
}
let storeFormItems = computed(() => useStore.state.FormItems.items)

// 在默认情况下不重新设置vuex中的数据,以防循环依赖
if (props.formData !== useStore.state.FormData) {
    // 设置vuex中的formData
    useStore.dispatch(FormDataEnum.ModuleFunctionName(FormDataEnum.RESET_FORM_DATA), props.formData)
}
let storeFormData = computed(() => useStore.state.FormData.data)

// 重新整理数据
let sideFormItems: any = {}
for (let key in useStore.state) {
    if (key.includes('/')) {
        let keyArr: Array<string> = key.split('/')
        if (!sideFormItems[keyArr[0]]) {
            sideFormItems[keyArr[0]] = []
        }
        // @ts-ignore
        sideFormItems[keyArr[0]].push(useStore.state[key])
    }
}

// 将其它放在最后
const tempOtherItems = sideFormItems.otherItems
delete sideFormItems.otherItems
sideFormItems.otherItems = tempOtherItems

// 要预览的代码
const previewCodes = ref('')
// 显示预览
const dialogPreview = ref(false)
// 显示代码预览
const dialogCodePreview = ref(false)
// 显示表单数据
const dialogGetFormData = ref(false)
// 显示列表数据
const dialogGetListData = ref(false)
// 显示表单配置JSON
const dialogGetFormConfig = ref(false)
// 显示表单配置JSON
const dialogImportJson = ref(false)
// 右侧设置tab当前选中
const activeName = ref('controlSetting')
// 右侧设置tab当前选中
const importJsonData = ref('')
// 右侧设置tab当前选中
const selectItem: ComputedRef<any> = computed(() => useStore.state.FormItems.currentItem)
// 右侧设置tab当前选中
const changeItem: ComputedRef<boolean> = computed(() => useStore.state.FormItems.changeItem)

// 复制默认的表单配置，作为右侧设置表单的配置
const formSetting = computed(() => useStore.state.FormConfig)
const formConfig = JSON.parse(JSON.stringify(formSetting.value))

interface KeyTexts {
    baseItems: string
    otherItems: string
    selectItems: string
}

const keyTexts: KeyTexts = {
    baseItems: '基础控件',
    otherItems: '其它控件',
    selectItems: '控件选择',
}

// 获取表单数据
const getFormData = () => {
    dialogGetFormData.value = true
}

// 获取表单项列表json
const getListData = () => {
    dialogGetListData.value = true
}

// 获取表单配置
const getFormConfig = () => {
    dialogGetFormConfig.value = true
}

// 预览表单
const previewForm = () => {
    dialogPreview.value = true
}

// 预览表单代码
const previewCode = () => {
    dialogCodePreview.value = true
    previewCodes.value = getPreviewCode()
}

const downloadForm = () => {
    let urlObject = window.URL || window.webkitURL || window
    previewCodes.value = getPreviewCode()
    let export_blob = new Blob([previewCodes.value])
    let save_link: any = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
    save_link.href = urlObject.createObjectURL(export_blob)
    save_link.download = 'FormDesigner.vue'
    let ev = document.createEvent('MouseEvents')
    ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
    save_link.dispatchEvent(ev)
}

// 获取预览代码
const getPreviewCode = (): string => {
    /* eslint-disable */
    return `
<script setup lang="ts">
    import { ref } from 'vue'
    const formData = ref(${JSON.stringify(storeFormData.value)})
    const formItems = ref(${JSON.stringify(storeFormItems.value)})
    const formSettings = ref(${JSON.stringify(formSetting.value)})
    // 提交事件需添加提交类型的按钮方可触发(触发方式：@click)
    const submit = () => {}
<\/script>
<template>
    <Designer :formItems="formItems" :preview="true" :formData="formData" :formConfig="formSettings" @submit="submit" />
<\/template>
        `
    /* eslint-enable */
}

// 点击某个表单项时触发
const getFormItem = () => {
    // 如果是切换组件，则暂时屏蔽监听
    useStore.dispatch(FormItemsEnum.ModuleFunctionName(FormItemsEnum.RELOAD_CHANGE_ITEM))
}

// 监听modelValue的变化
watch(
    () => selectItem.value.modelValue,
    (newVal, oldVal) => {
        if (changeItem.value && newVal) {
            storeFormData.value[newVal] = storeFormData.value[oldVal]
            delete storeFormData.value[oldVal]
        }
    }
)

const designer: Ref<HTMLElement | null> = ref<HTMLElement | null>(null)

// 获取表单实例
const getDynamicForm = (): HTMLElement | null => {
    // @ts-ignore
    return designer.value.dynamicForm
}

// eslint-disable-next-line no-undef
defineExpose({
    getDynamicForm,
})

// 清空
const clearData = () => {
    // @ts-ignore
    // eslint-disable-next-line vue/no-mutating-props
    props.formItems.length = 0
    useStore.dispatch(FormItemsEnum.ModuleFunctionName(FormItemsEnum.SET_CURRENT_ITEM), {})
    for (let key in storeFormData.value) {
        // eslint-disable-next-line no-prototype-builtins
        if (storeFormData.value.hasOwnProperty(key)) {
            delete storeFormData.value[key]
        }
    }
}

// 导入列
const importData = () => {
    let data = []
    try {
        data = JSON.parse(importJsonData.value)
        // eslint-disable-next-line no-empty
    } catch (e) {}
    if (Array.isArray(data)) {
        data.forEach((e) => {
            if (e.type === 'Checkbox') {
                initItemData({ key: e.modelValue, value: [] })
            }
            if (e.type === 'InputNumber') {
                initItemData({ key: e.modelValue, value: 0 })
            }
            storeFormItems.value.push(e)
        })
    } else {
        ElMessage.error('请输入数组格式的JSON')
    }
    dialogImportJson.value = false
    importJsonData.value = ''
}

// 初始化表单的特殊字段双向绑定属性
const initItemData = (e: any): void => {
    // @ts-ignore
    storeFormData[e.key] = e.value
    if (!e.key) {
        return
    }
    // 移除原先存在的key
    let keys: Array<string> = e.key.split('_')
    if (!keys || keys.length !== 2) {
        return
    }
    // @ts-ignore
    delete storeFormData[keys[0]]
}

// 导入列
const onImportInput = () => {
    if (importJsonData.value === '') {
        return
    }
    try {
        importJsonData.value = JSON.stringify(JSON.parse(importJsonData.value), null, 4)
    } catch (e) {
        ElMessage.error('输入json格式有误，请检查后重新输入')
    }
}

</script>

<template>
    <el-row :gutter="0" :style="{ height }">
        <el-col :span="6">
            <el-card shadow="never" class="container-left">
                <el-scrollbar :height="height">
                    <el-form
                        :aria-readonly="true"
                        ref="dynamicForm"
                        label-position="top"
                    >
                        <div v-for="(item, key, i) in sideFormItems" :key="key + i">
                            <div class="form-item-title">
                                {{ keyTexts[key] }}
                            </div>

                            <draggable
                                :sort="false"
                                :list="item"
                                :group="{
                                    name: 'people',
                                    pull: 'clone',
                                    put: false,
                                }"
                                class="form-item-block"
                            >
                                <div v-for="(item1, i1) in item" :key="item1.modelValue" class="control-block">
                                    <label class="el-form-item__label" v-if="keyTexts[key] === '其它控件'">{{ item1.label }}</label>
                                    <ItemResolve
                                        :formItem="item1"
                                        :preview="true"
                                        :formData="{
                                            Checkbox: [],
                                            InputNumber: 0
                                        }"
                                        :dynamicForm="null"
                                    />
                                </div>
                            </draggable>
                        </div>
                    </el-form>
                </el-scrollbar>
            </el-card>
        </el-col>
        <el-col :span="12">
            <el-card shadow="never" class="container-left generator-container">
                <div class="generator-tool-bar">
                    <div class="generator-title">编辑区</div>
                    <div>
                        <el-button type="text" @click="clearData">清空</el-button>
                        <el-button type="text" @click="dialogImportJson = true">导入</el-button>
                        <el-button type="text" @click="downloadForm">下载</el-button>
                        <el-button type="text" @click="previewForm">预览</el-button>
                        <el-button type="text" @click="previewCode">代码预览</el-button>
                        <el-button type="text" @click="getFormConfig">表单配置</el-button>
                        <el-button type="text" @click="getFormData">表单数据</el-button>
                        <el-button type="text" @click="getListData">表单项列表</el-button>
                    </div>
                </div>
                <el-scrollbar
                    height="100%"
                    style="padding: var(--el-card-padding) var(--el-card-padding) 0 var(--el-card-padding)"
                >
                    <Designer
                        ref="designer"
                        :formItems="storeFormItems"
                        :height="height"
                        :preview="preview"
                        :formData="storeFormData"
                        :formConfig="formSetting"
                        @submit="submit"
                        @selectItem="getFormItem"
                    />
                </el-scrollbar>
            </el-card>
        </el-col>
        <el-col :span="6">
            <el-card shadow="never" class="container-left generator-container">
                <el-tabs v-model="activeName">
                    <el-tab-pane label="控件设置" name="controlSetting">
                        <el-scrollbar
                            style="padding: var(--el-card-padding) var(--el-card-padding) 0 var(--el-card-padding)"
                        >
                            <ControlConfig :style="{ height: `calc(${height} - 70px)` }" />
                        </el-scrollbar>
                    </el-tab-pane>
                    <el-tab-pane
                        label="表单设置"
                        name="formSetting"
                        style="padding: var(--el-card-padding) var(--el-card-padding) 0 var(--el-card-padding)"
                    >
                        <Designer
                            :formItems="formSettingItems"
                            :height="`calc(${height} - 50px)`"
                            :preview="true"
                            :formData="formSetting"
                            :formConfig="formConfig"
                        />
                    </el-tab-pane>
                </el-tabs>
            </el-card>
        </el-col>
    </el-row>
    <el-dialog v-model="dialogPreview" title="Preview" fullscreen destroy-on-close append-to-body>
        <Designer
            :formItems="storeFormItems"
            :height="height"
            :preview="true"
            :formData="storeFormData"
            :formConfig="formSetting"
        />
    </el-dialog>
    <el-dialog v-model="dialogCodePreview" title="Preview" fullscreen destroy-on-close append-to-body>
        <pre v-highlightjs="previewCodes"><code class="autodetect"></code></pre>
    </el-dialog>
    <el-dialog v-model="dialogGetFormData" title="formData" fullscreen destroy-on-close append-to-body>
        <json-viewer :value="storeFormData" copyable boxed sort />
    </el-dialog>
    <el-dialog v-model="dialogGetListData" title="listData" fullscreen destroy-on-close append-to-body>
        <json-viewer :value="storeFormItems" copyable boxed sort />
    </el-dialog>
    <el-dialog v-model="dialogGetFormConfig" title="formSetting" fullscreen destroy-on-close append-to-body>
        <json-viewer :value="formSetting" copyable boxed sort />
    </el-dialog>
    <el-dialog v-model="dialogImportJson" title="输入表单列JSON" width="30%" append-to-body>
        <span
            ><el-input
                v-model="importJsonData"
                @input="onImportInput"
                :rows="15"
                type="textarea"
                placeholder="Please input"
        /></span>
        <template #footer>
            <span class="dialog-footer">
                <el-button @click="dialogImportJson = false">取消</el-button>
                <el-button type="primary" @click="importData">确定</el-button>
            </span>
        </template>
    </el-dialog>
</template>

<style scoped>
.control-block ::v-deep(.el-range-editor--medium.el-input__inner) {
    width: 95%;
}
.control-block {
    margin: 6px;
    padding: 10px 20px;
}
.control-block:hover {
    border-radius: 5px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)
}
::v-deep(.el-tabs__nav-scroll) {
    padding-left: var(--el-card-padding);
}
.form-item-title {
    font-size: 15px;
    font-weight: bold;
    margin: 3% 3% 5% 0;
}

.form-item-block {
    width: 100%;
    /*display: flex;
    flex-wrap: wrap;
    align-content: flex-start;*/
}

.generator-title {
    font-size: 15px;
    font-weight: bold;
}

.generator-container ::v-deep(.el-card__body) {
    padding: 0 !important;
}

.generator-tool-bar {
    height: 39px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 var(--el-card-padding);
    border-bottom: 1px solid var(--el-border-color-base);
}

.el-card {
    margin: 0;
    border-radius: 0;
    padding-bottom: 0;
}

.el-row {
    margin: 0 !important;
}

::v-deep(.el-card__body) {
    padding-bottom: 0;
}

.el-col {
    padding: 0;
}

.el-row:last-child {
    margin-bottom: 0;
}

.container-left {
    height: 100%;
}

.el-form .width-1 .sortable-chosen {
    width: 100%;
}

.el-form .width-2 .sortable-chosen {
    width: 50%;
}

.el-form .width-3 .sortable-chosen {
    width: 33.33%;
}

.el-form .width-4 .sortable-chosen {
    width: 25%;
}

.el-form .width-5 .sortable-chosen {
    width: 20%;
}

.el-form .width-6 .sortable-chosen {
    width: 16.66%;
}

/*.el-form .sortable-chosen {
    width: 100%;
    font-size: 0;
    border: none;
    line-height: 0;
    overflow: hidden;
    border-top: 1px dashed var(--el-color-primary);
}*/

.form-item-control {
    width: 45%;
    cursor: move;
    line-height: 30px;
    margin: 0 3% 3% 0;
    text-align: center;
    font-size: var(--el-font-size-base);
    border: 1px solid var(--el-border-color-base);
}

.form-item-control:hover {
    color: var(--el-color-primary);
    border-color: var(--el-color-primary);
}

.container-left .container-item {
    min-height: 20px;
    padding: 20px 0 !important;
    border: 1px dashed var(--el-border-color-base) !important;
}

.container-left ::v-deep(.container-item .el-col) {
    border: 1px dashed var(--el-border-color-base) !important;
}
</style>
